using System;
using System.Collections.Generic;
using System.Linq;
using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using AutoMapper;
using Hangfire;
using Hangfire.Common;
using Hangfire.States;
using Hangfire.Storage;
using Hangfire.Storage.Monitoring;
using L._52ABP.Application.Dtos;
using L._52ABP.Application.HangFire.Dtos;
using L._52ABP.Application.HangFire.Enums;

namespace Yozeev.Service
{
    public class HangfireService : YozeevAppServiceBase,IHangfireService
    {
        //private readonly IBackgroundJobManager _backgroundJobManager;
        private IBackgroundJobClient _backgroundJobClient;

        public HangfireService(IBackgroundJobClient backgroundJobClient)
        {
            _backgroundJobClient = backgroundJobClient;
        }


        /// <summary>
        /// 获取所有周期性作业
        /// </summary>
        /// <returns></returns>
        public RecurringResultList GetAllRecurringJob(PagedSortedAndFilteredInputDto input)
        {
            var listColumnHeaders = new List<ColumnHeader> {
                new ColumnHeader { PropertyName="recurringJobId" },
                new ColumnHeader { PropertyName="queue" },
                new ColumnHeader { PropertyName="name" },
                new ColumnHeader { PropertyName="cron" },
                new ColumnHeader { PropertyName="lastExecution",ColumnType=ColumnType.dateType },
                new ColumnHeader { PropertyName="lastJobId" },
                new ColumnHeader { PropertyName="lastJobState" },
                new ColumnHeader { PropertyName="nextExecution",ColumnType=ColumnType.dateType },
                new ColumnHeader { PropertyName="timeZoneId" },
                new ColumnHeader { PropertyName="jobName" },
                new ColumnHeader { PropertyName="createdAt",ColumnType=ColumnType.dateType },
            };

            var totalCount = 0;
            var list = new List<RecurringListJobDto>();

            using (var connection = JobStorage.Current.GetConnection())
            {
                var storageConnection = connection as JobStorageConnection;
                if (storageConnection != null)
                {
                    totalCount = (int)storageConnection.GetRecurringJobCount();

                    var recurringJobs = storageConnection.GetRecurringJobs();

                    list = recurringJobs.Skip(input.SkipCount).Take(input.MaxResultCount).Select(a => new RecurringListJobDto
                    {
                        RecurringJobId = a.Id,
                        Queue = a.Queue,
                        Name = a.Job?.Method.Name,
                        Cron = a.Cron,
                        LastExecution = a.LastExecution,
                        LastJobId = a.LastJobId,
                        LastJobState = a.LastJobState,
                        NextExecution = a.NextExecution,
                        TimeZoneId = a.TimeZoneId,
                        JobName = $"{a.Job?.Type.Name}.{a.Job?.Method.Name}",
                        CreatedAt = a.CreatedAt

                    }).ToList();
                }
                return new RecurringResultList
                {
                    RecurringListJobDtos = new PagedResultDto<RecurringListJobDto>(totalCount, list),
                    ColumnHeaders = listColumnHeaders
                };
            }
        }

        /// <summary>
        /// 获取所有作业
        /// </summary>
        public PagedResultDto<HangFireStorageJob> GetAllJobs(JobInput input)
        {
            var query = JobStorage.Current.GetMonitoringApi();
            var list = new List<HangFireStorageJob>();
            var totalCount = 0;
            switch (input.JobState)
            {
                case JobState.Success: //完成
                    totalCount = (int)query.SucceededListCount();
                    list = query.SucceededJobs(input.SkipCount, input.MaxResultCount).Select(a => new HangFireStorageJob
                    {
                        Key = a.Key,
                        State = a.Value.InSucceededState,
                        CreateAt = a.Value.SucceededAt,
                        TotalDuration = a.Value.TotalDuration,
                        JobName = $"{a.Value.Job?.Type.Name}.{a.Value.Job?.Method.Name}",
                    }).ToList();
                    break;
                case JobState.Delete: //删除
                    totalCount = (int)query.DeletedListCount();
                    list = query.DeletedJobs(input.SkipCount, input.MaxResultCount).Select(a => new HangFireStorageJob
                    {
                        Key = a.Key,
                        State = a.Value.InDeletedState,
                        CreateAt = a.Value.DeletedAt,
                        JobName = $"{a.Value.Job?.Type.Name}.{a.Value.Job?.Method.Name}",
                    }).ToList();
                    break;
                case JobState.Faile: //失败
                    totalCount = (int)query.FailedCount();
                    list = query.FailedJobs(input.SkipCount, input.MaxResultCount).Select(a => new HangFireStorageJob
                    {
                        Key = a.Key,
                        State = a.Value.InFailedState,
                        CreateAt = a.Value.FailedAt,
                        JobName = $"{a.Value.Job?.Type.Name}.{a.Value.Job?.Method.Name}",
                        ExceptionMessage = a.Value.ExceptionMessage,
                        ExceptionType = a.Value.ExceptionType,
                        ExceptionDetails = a.Value.ExceptionDetails,
                        Reson = a.Value.Reason,
                    }).ToList();
                    break;
                case JobState.Plan: //计划
                    totalCount = (int)query.ScheduledCount();
                    list = query.ScheduledJobs(input.SkipCount, input.MaxResultCount).Select(a => new HangFireStorageJob
                    {
                        Key = a.Key,
                        State = a.Value.InScheduledState,
                        CreateAt = a.Value.ScheduledAt,
                        EnqueueAt = a.Value.EnqueueAt,
                        JobName = $"{a.Value.Job?.Type.Name}.{a.Value.Job?.Method.Name}"
                    }).ToList();
                    break;
                case JobState.Runing: //执行中
                    totalCount = (int)query.ProcessingCount();
                    list = query.ProcessingJobs(input.SkipCount, input.MaxResultCount).Select(a => new HangFireStorageJob
                    {
                        Key = a.Key,
                        State = a.Value.InProcessingState,
                        CreateAt = a.Value.StartedAt,
                        ServerId = a.Value.ServerId,
                        JobName = $"{a.Value.Job?.Type.Name}.{a.Value.Job?.Method.Name}"
                    }).ToList();
                    break;
                case JobState.Enqueued:
                    totalCount = query.Queues().Count();
                    list = query.Queues().Select(a => new HangFireStorageJob
                    {
                        QueueName = a.Name,
                        Length = a.Length,
                        Fetched = a.Fetched,
                        FirstJobs = a.FirstJobs.Select(b => new EnqueuedJobListDto
                        {
                            Key = b.Key,
                            CreateAt = b.Value.EnqueuedAt,
                            JobName = $"{b.Value.Job?.Type.Name}.{b.Value.Job?.Method.Name}",
                            ResultState = b.Value.State
                        }).ToList()
                    }).ToList();
                    break;
                case JobState.EnqueuedJob:
                    totalCount = (int)query.EnqueuedCount(input.QueueName);
                    list = query.EnqueuedJobs(input.QueueName, input.SkipCount, input.MaxResultCount).Select(a => new HangFireStorageJob
                    {
                        Key = a.Key,
                        State = a.Value.InEnqueuedState,
                        ResultState = a.Value.State,
                        CreateAt = a.Value.EnqueuedAt,
                        JobName = $"{a.Value.Job?.Type.Name}.{a.Value.Job?.Method.Name}"
                    }).ToList();
                    break;
                case JobState.Fetched:
                    totalCount = (int)query.FetchedCount(input.QueueName);
                    list = query.FetchedJobs(input.QueueName, input.SkipCount, input.MaxResultCount).Select(a => new HangFireStorageJob
                    {
                        Key = a.Key,
                        ResultState = a.Value.State,
                        CreateAt = a.Value.FetchedAt,
                        JobName = $"{a.Value.Job?.Type.Name}.{a.Value.Job?.Method.Name}"
                    }).ToList();
                    break;
                case JobState.Waiting: //等待中
                    using (var connection = JobStorage.Current.GetConnection())
                    {
                        var storageConnection = connection as JobStorageConnection;
                        if (storageConnection != null)
                        {
                            totalCount = (int)storageConnection.GetSetCount("awaiting");
                            var jobIds = storageConnection.GetRangeFromSet("awaiting", input.SkipCount, input.MaxResultCount);
                            list = jobIds.Select(a =>
                            {
                                var jobData = connection.GetJobData(a);
                                var stateData = connection.GetStateData(a);
                                var parentData = connection.GetStateData(stateData?.Data?["ParentId"]);
                                return new HangFireStorageJob
                                {
                                    Key = a,
                                    ResultState = jobData?.State,
                                    Options = stateData?.Data?["Options"],
                                    ParentId = stateData?.Data?["ParentId"],
                                    ParentName = parentData?.Name,
                                    Reson = stateData?.Reason,
                                    JobName = $"{jobData.Job?.Type.Name}.{jobData.Job?.Method.Name}",
                                    CreateAt = jobData?.CreatedAt,
                                    RetryAt = stateData != null && stateData.Data.ContainsKey("EnqueueAt")
                                      ? JobHelper.DeserializeDateTime(stateData.Data["EnqueueAt"]) : new DateTime?()
                                };
                            }).ToList();
                        }
                    }
                    break;
            }
            return new PagedResultDto<HangFireStorageJob>(
                    totalCount,
                    list
                );
        }
        /// <summary>
        /// 获取所有服务
        /// </summary>
        /// <returns></returns>
        public ServerResultList GetServer()
        {
            var listColumnHeaders = new List<ColumnHeader> {
                new ColumnHeader { PropertyName="name" },
                new ColumnHeader { PropertyName="workersCount" },
                new ColumnHeader { PropertyName="queues",ColumnType=ColumnType.listType },
                new ColumnHeader { PropertyName="startedAt",ColumnType=ColumnType.dateType },
                new ColumnHeader { PropertyName="heartbeat",ColumnType=ColumnType.dateType }
            };
            return new ServerResultList
            {
                HangfireServerListDtos = JobStorage.Current.GetMonitoringApi().Servers().Select(a => new HangfireServerListDto
                {
                    Name = a.Name,
                    StartedAt = a.StartedAt,
                    Queues = a.Queues,
                    WorkersCount = a.WorkersCount,
                    Heartbeat = a.Heartbeat,
                }).ToList(),
                ColumnHeaders = listColumnHeaders
            };
        }
        /// <summary>
        /// 查看作业详情信息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public HangfireJobDetailDto GetJobDetail(EntityDto<string> input)
        {
            var result = JobStorage.Current.GetMonitoringApi().JobDetails(input.Id);
            result.History.Add(new StateHistoryDto { StateName = "Created", CreatedAt = result.CreatedAt ?? default(DateTime) });
            var dto = new HangfireJobDetailDto
            {
                CreatedAt = result.CreatedAt,
                ExpireAt = result.ExpireAt,
                History = result.History,
                Namespace = result.Job?.Type.Namespace,
                Properties = result.Properties,
                ClassName = result.Job?.Type.Name,
                MethodName = result.Job?.Method.Name,
                JobName = $"{result.Job?.Type.Name}.{result.Job?.Method.Name}"
            };
            return dto;
        }

        /// <summary>
        /// 删除周期性作业
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public bool DeleteRecurringJob(RecurringJobInput input)
        {
            if (input.JobId.Any())
            {
                foreach (var item in input.JobId)
                {
                    RecurringJob.RemoveIfExists(item);
                }
                return true;
            }
            return false;
        }

        /// <summary>
        /// 执行作业
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public bool RunRecurringJob(RecurringJobInput input)
        {
            //IRecurringJobManager
            if (input.JobId.Any())
            {
                foreach (var item in input.JobId)
                {
                    RecurringJob.Trigger(item);
                }
                return true;
            }
            return false;
        }
        /// <summary>
        /// 重新加入队列
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public bool Requeue(RecurringJobInput input)
        {
            if (input.JobId.Any())
            {
                foreach (var item in input.JobId)
                {
                    BackgroundJob.Requeue(item);
                }
                return true;
            }
            return false;
        }
        /// <summary>
        /// 删除普通作业
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public bool DeleteJob(RecurringJobInput input)
        {
            if (input.JobId.Any())
            {
                foreach (var item in input.JobId)
                {
                    BackgroundJob.Delete(item);
                }
                return true;
            }
            return false;
        }
        /// <summary>
        /// 获取Dashboard仪表盘-数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public DashboardIndexDetailDto GetDashboardData(DashboardIndexDto input)
        {
            List<DashboardDataDto> result = new List<DashboardDataDto>();
            IDictionary<DateTime, long> succeeded = null;
            IDictionary<DateTime, long> failed = null;
            var monitor = JobStorage.Current.GetMonitoringApi();
            if (input.SearchTimeType == SearchTimeType.week)
            {
                succeeded = monitor.SucceededByDatesCount();
                failed = monitor.FailedByDatesCount();
            }
            else if (input.SearchTimeType == SearchTimeType.day)
            {
                succeeded = monitor.HourlySucceededJobs();
                failed = monitor.HourlyFailedJobs();
            }
            if (succeeded != null)
            {
                var success = succeeded.Keys.Select(a => new DashboardDataDto
                {
                    Key = a,
                    Type = "成功",
                    Value = succeeded[a].ToString()
                }).ToList();
                result.AddRange(success);
            }
            if (failed != null)
            {
                var faile = failed.Keys.Select(a => new DashboardDataDto
                {
                    Key = a,
                    Type = "失败",
                    Value = failed[a].ToString()
                }).ToList();
                result.AddRange(faile);
            }
            return new DashboardIndexDetailDto { DashboardDataDtos = result.OrderBy(a => a.Key).ToList() };
        }
        /// <summary>
        /// 获取统计数据
        /// </summary>
        /// <returns></returns>
        public StatisticsInfoDto GetDashboardStatistics()
        {


            var result = JobStorage.Current.GetMonitoringApi().GetStatistics();

            var dto = ObjectMapper.Map<StatisticsInfoDto>(result);
        

            using (var connection = JobStorage.Current.GetConnection())
            {
                if (connection is JobStorageConnection storageConnection)
                {
                    dto.Retries = (int)storageConnection.GetSetCount("retries");
                    dto.Waiting = (int)storageConnection.GetSetCount("waiting");
                    dto.DateValue = $"{DateTime.Now.Hour}:{DateTime.Now.Minute}:{DateTime.Now.Second}";
                }
            }
            return dto;
        }

        /// <summary>
        /// 获取重试的作业数据
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public RetriesResultList GetRetriesJob(PagedSortedAndFilteredInputDto input)
        {
            var totalCount = 0;
            var list = new List<RetriesJobListDto>();
            using (var connection = JobStorage.Current.GetConnection())
            {
                var storageConnection = connection as JobStorageConnection;
                if (storageConnection != null)
                {
                    totalCount = (int)storageConnection.GetSetCount("retries");
                    var jobIds = storageConnection.GetRangeFromSet("retries", input.SkipCount, input.MaxResultCount);
                    list = jobIds.Select(a =>
                      {
                          var jobData = connection.GetJobData(a);
                          var stateData = connection.GetStateData(a);
                          return new RetriesJobListDto
                          {
                              JobId = a,
                              State = jobData?.State,
                              Reason = stateData?.Reason,
                              JobName = $"{jobData?.Job?.Type.Name}.{jobData?.Job?.Method.Name}",
                              CreatedTime = jobData?.CreatedAt,
                              RetryTime = stateData != null && stateData.Data.ContainsKey("EnqueueAt")
                                ? JobHelper.DeserializeDateTime(stateData.Data["EnqueueAt"]) : new DateTime?()
                          };
                      }).ToList();
                }

                var listColumnHeaders = new List<ColumnHeader> {
                    new ColumnHeader { PropertyName="jobId",ColumnType=ColumnType.link },
                    new ColumnHeader { PropertyName="state" },
                    new ColumnHeader { PropertyName="reason" },
                    new ColumnHeader { PropertyName="jobName" },
                    new ColumnHeader { PropertyName="createdTime",ColumnType=ColumnType.dateType },
                    new ColumnHeader { PropertyName="retryTime" ,ColumnType=ColumnType.dateType}
                            };
                return new RetriesResultList
                {
                    RetriesJobListDtos = new PagedResultDto<RetriesJobListDto>(totalCount, list),
                    ColumnHeaders = listColumnHeaders
                };
            }
        }

        /// <summary>
        /// 加入队列
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public bool AddEnqueueJob(RecurringJobInput input)
        {
            if (input.JobId.Any())
            {
                foreach (var item in input.JobId)
                {
                    _backgroundJobClient.ChangeState(item, CreateEnqueuedState(), ScheduledState.StateName);
                }
                return true;
            }
            return false;
        }
        private static EnqueuedState CreateEnqueuedState()
        {
            return new EnqueuedState { Reason = "Triggered via Dashboard UI" };
        }
    }
}